#include "KernelManager.h"

#include <HybridRenderer\Scene\Cube.h>
#include "..\Scene\Volume.h"

using namespace Kernel;

ManagerPtr Manager::singleton;

ManagerPtr Manager::getSingleton()
{
	if(singleton == nullptr)
		singleton = std::make_shared<Manager>();

	return singleton;
}

Manager::Manager()
{
	parser = std::make_shared<TCODParser>();
}

Manager::~Manager()
{
}

int Manager::configure(const std::string &base_dir)
{
	this->base_dir = base_dir;
	CfgParsers::Engine::CfgParser::run(parser, base_dir+"config\\Engine.cfg", engine_data);
	return 0;
}

int Manager::initialize(int argc, char **argv)
{
	shader_loader = std::make_shared<File::ShaderLoader>(base_dir+"shaders\\");
	tex_loader = std::make_shared<File::TextureLoader>(base_dir+"textures\\");
	mat_loader = std::make_shared<File::MaterialLoader>(base_dir+"materials\\");

	g_buffer = std::make_shared<Render::GBuffer>(shader_loader, getWidth(), getHeight());
	renderer = std::make_shared<Render::DeferredRender>(g_buffer, shader_loader, getWidth(), getHeight());

	//auto terrain_atlas = tex_loader->load_array("array.png", 16,16,2,2, GL_REPEAT);
	//auto terrain_atlas = tex_loader->load_array("terminal.png", 16,16,16,16, GL_REPEAT);
	auto terrain_atlas = tex_loader->load("terminal2.png", GL_REPEAT);
	auto terrain_sampler_uni = std::make_shared<Render::Uniform>(g_buffer->getShader()->getFS(), "diffuse_tex");
	auto terrain_sampler = std::make_shared<Render::Sampler>(GL_REPEAT);

	auto basic_mat = renderer->addMaterial(mat_loader->load("basic.mat"));

	scene = std::make_shared<Scene::SceneManager>();

	camera = Scene::Camera::getSingleton();
	camera->init(getWidth(), getHeight(), M_PI/5.0f, getNearClip(), getFarClip());
	camera->setDirection(Eigen::Vector3f(0.0f, -0.6f, 1.0f));
	
	Scene::VolumePtr volume;
	volume = std::make_shared<Scene::Volume>();
	{
		volume->setMVP(	g_buffer->getMVP());
		volume->setMV(	g_buffer->getMV());
		volume->setN_WRI(	g_buffer->getN_WRI());
		volume->setMaterial(basic_mat);
		volume->setTexture(terrain_atlas, terrain_sampler_uni, terrain_sampler);
		scene->add(volume);
		volume->setPosition( Eigen::Vector3f(200,-180,200) );
	}

	return 0;
}

void Manager::input(unsigned char key, int x, int y)
{
	static float speed = 1.0f;
	switch(key)
	{
	case 'z':
		{
			camera->setPosition(Eigen::Vector3f(camera->getPosition().x(), camera->getPosition().y(), camera->getPosition().z()+speed));
		}break;
	case 'x':
		{
			camera->setPosition(Eigen::Vector3f(camera->getPosition().x(), camera->getPosition().y(), camera->getPosition().z()-speed));
		}break;

	case 'a':
		{
			camera->setPosition(Eigen::Vector3f(camera->getPosition().x()-speed, camera->getPosition().y(), camera->getPosition().z()));
		}break;
	case 'd':
		{
			camera->setPosition(Eigen::Vector3f(camera->getPosition().x()+speed, camera->getPosition().y(), camera->getPosition().z()));
		}break;

	case 'w':
		{
			camera->setPosition(Eigen::Vector3f(camera->getPosition().x(), camera->getPosition().y()-speed, camera->getPosition().z()));
		}break;
	case 's':
		{
			camera->setPosition(Eigen::Vector3f(camera->getPosition().x(), camera->getPosition().y()+speed, camera->getPosition().z()));
		}break;
	case '1':
		{
			speed--;
		}break;
	case '2':
		{
			speed++;
		}break;
	case 'r':
		{
			g_buffer->reloadShader();
		}
	};
}

void Manager::update(float dt)
{
	camera->updateView();
}

void Manager::render()
{
	glClearColor(0.f,0.f,0.f,1.f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );


	//Rasterize
	glEnable(GL_DEPTH_TEST);
	g_buffer->begin();
	scene->render(g_buffer->getShader());
	g_buffer->end();
	glDisable(GL_DEPTH_TEST);
	renderer->render();
}
